package com.zxy.ziems.server.scene.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.zxy.ziems.server.device.pojo.enums.AlarmTreeFilterEnum;
import com.zxy.ziems.server.file.entity.FileEntity;
import com.zxy.ziems.server.file.service.FileService;
import com.zxy.ziems.server.file.vo.FileVO;
import com.zxy.ziems.server.mybatis.core.query.QueryWrapperX;
import com.zxy.ziems.server.mybatis.core.query.WrapperX;
import com.zxy.ziems.server.mybatis.core.service.ServiceImplX;
import com.zxy.ziems.server.scene.convertor.SceneConvertor;
import com.zxy.ziems.server.scene.mapper.SceneMapper;
import com.zxy.ziems.server.scene.pojo.bo.Frame;
import com.zxy.ziems.server.scene.pojo.dto.SceneDTO;
import com.zxy.ziems.server.scene.pojo.entity.SceneEntity;
import com.zxy.ziems.server.scene.pojo.enums.SceneType;
import com.zxy.ziems.server.scene.pojo.param.SceneTreeParam;
import com.zxy.ziems.server.scene.pojo.vo.SceneTreeVO;
import com.zxy.ziems.server.scene.pojo.vo.SceneVO;
import com.zxy.ziems.server.scene.service.SceneService;
import com.zxy.ziems.server.utils.AssertUtil;
import com.zxy.ziems.server.utils.BusinessExceptionUtil;
import com.zxy.ziems.server.utils.tree.TreeUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author chdzq
 * @create 2023/3/21
 */
@Service
public class SceneServiceImpl extends ServiceImplX<SceneMapper, SceneEntity> implements SceneService {


    @Override
    public SceneVO add(SceneDTO dto) {
        //check
        SceneEntity entity = doCheckBeforeSaveAction(dto);
        save(entity);
        return SceneConvertor.INSTANCE.INSTANCE.sceneDoConvert2VO(entity);
    }

    private SceneEntity doCheckBeforeSaveAction(SceneDTO dto) {
        if (StrUtil.isNotBlank(dto.getId())) {
            SceneEntity scene = getById(dto.getId());
            dto.setParentId(scene.getParentId());
            AssertUtil.isTrue(Objects.nonNull(scene), "场景不存在");
            if (Objects.isNull(dto.getType())) {
                dto.setType(dto.getType());
            } else {
                AssertUtil.isTrue(Objects.equals(scene.getType(), dto.getType()), "场景类型不能变更");
            }
        }

        if (StrUtil.isBlank(dto.getParentId())) {
            dto.setParentId("0");
            SceneEntity entity = this.getBuildingByCode(dto.getCode());
            AssertUtil.isNull(entity, "楼栋编码已存在");
        }

        if (!Objects.equals("0", dto.getParentId())) {
            SceneEntity parent = getById(dto.getParentId());
            if (null == parent) {
                throw BusinessExceptionUtil.exception("所属上级不存在");
            }
        }

        if (Objects.equals(dto.getType(), SceneType.FLOOR.getCode())) {
            SceneEntity parent = getById(dto.getParentId());
            if (Objects.isNull(parent) || !Objects.equals(parent.getType(), SceneType.BUILD.getCode())) {
                throw BusinessExceptionUtil.exception("所属上级不是楼栋");
            }
        }
        if (Objects.isNull(dto.getFrame())) {
            Frame frame = new Frame();
            frame.setX(BigDecimal.ZERO);
            frame.setY(BigDecimal.ZERO);
            frame.setZ(BigDecimal.ZERO);
            frame.setWidth(dto.getWidth());
            frame.setHeight(dto.getHeight());
            dto.setFrame(frame);
        }
        SceneEntity entity = SceneConvertor.INSTANCE.sceneDTOConvert2DO(dto);
        return entity;
    }

    /**
     * 获取楼栋
     * @param code
     * @return
     */
    private SceneEntity getBuildingByCode(String code) {
        return baseMapper.selectOne(SceneEntity::getCode, code, SceneEntity::getParentId, "0");
    }

    @Override
    public SceneVO update(SceneDTO dto) {
        SceneEntity entity = doCheckBeforeSaveAction(dto);
        updateById(entity);
        return SceneConvertor.INSTANCE.INSTANCE.sceneDoConvert2VO(entity);
    }

    @Override
    public void deleteById(String id) {
        removeById(id);
    }

    @Override
    public SceneEntity getById(String id) {
        return super.getById(id);
    }

    @Override
    public List<SceneEntity> listByIds(Collection<? extends Serializable> ids) {
        if (CollectionUtil.isEmpty(ids)) {
            return new ArrayList<>(0);
        }
        return super.listByIds(ids);
    }

    @Override
    public List<SceneEntity> inheritListBy(String id) {
        return baseMapper.selectInheritListBy(id);
    }

    @Override
    public List<SceneEntity> allListByCode(String code) {
        return baseMapper.selectAllListByCode(code);
    }

    @Override
    public SceneVO viewById(String id) {
        SceneEntity entity = getById(id);
        AssertUtil.isTrue(Objects.nonNull(entity), "场景不存在");
        FileVO file = fileService.viewById(entity.getFileId());
        SceneVO vo = SceneConvertor.INSTANCE.sceneDOAndFileConvert2VO(entity, file);
        return vo;
    }

    @Override
    public SceneVO viewByCode(String code) {
        QueryWrapper<SceneEntity> queryWrapper = new QueryWrapperX();
        queryWrapper.lambda().eq(SceneEntity::getCode, code);
        SceneEntity entity = this.getOne(queryWrapper);


        AssertUtil.isTrue(Objects.nonNull(entity), "场景不存在");
        FileVO file = fileService.viewById(entity.getFileId());
        SceneVO vo = SceneConvertor.INSTANCE.sceneDOAndFileConvert2VO(entity, file);
        return vo;
    }

    @Override
    public List<SceneTreeVO> getTreeBy(SceneTreeParam param) {
        List<SceneEntity> sceneList = baseMapper.selectInheritListBy(param.getSceneId());
        if (CollectionUtil.isEmpty(sceneList)) {
            return new ArrayList<>(0);
        }

        List<String> fileIds = sceneList.stream().map(SceneEntity::getFileId).collect(Collectors.toList());
        List<FileEntity> fileEntities = fileService.listByIds(fileIds);
        Map<String, FileEntity> fileEntityMap = fileEntities.stream().collect(Collectors.toMap(FileEntity::getId, (a) -> a));
        List<SceneTreeVO> treeList = sceneList.stream().map((a) -> SceneConvertor.INSTANCE.sceneDOConvert2TreeVO(a, fileEntityMap.get(a.getFileId()))).collect(Collectors.toList());
        List<SceneTreeVO> tree = TreeUtils.tree(treeList);
        //过滤
        return tree;
    }


    @Override
    public List<SceneVO> listByParentId(String parentId) {
        if (StrUtil.isBlank(parentId)) {
            parentId = "0";
        }
        List<SceneEntity> sceneList = list(WrapperX.<SceneEntity>lambdaQuery().eq(SceneEntity::getParentId, parentId));

        if (CollectionUtil.isEmpty(sceneList)) {
            return new ArrayList<>();
        }

        List<String> fileIds = sceneList.stream().map(SceneEntity::getFileId).collect(Collectors.toList());
        List<FileEntity> fileEntities = fileService.listByIds(fileIds);
        Map<String, FileEntity> fileEntityMap = fileEntities.stream().collect(Collectors.toMap(FileEntity::getId, (a) -> a));
        List<SceneVO> list = sceneList.stream()
                .map((a) -> SceneConvertor.INSTANCE.sceneDoConvert2VO(a, fileEntityMap.get(a.getFileId())))
                .collect(Collectors.toList());
        return list;
    }

    @Override
    public List<FileVO> childSceneFileByCode(String code) {
        SceneEntity sceneEntity = getBuildingByCode(code);
        QueryWrapper<SceneEntity> query = new QueryWrapperX<>();
        query.lambda().eq(SceneEntity::getParentId, sceneEntity.getId());
        List<SceneEntity> sceneEntities = baseMapper.selectList(query);
        if (CollectionUtils.isEmpty(sceneEntities)) {
            throw new NullPointerException("未配置场景图片，请先配置图片");
        }
        List<FileEntity> fileEntities = fileService.listByIds(sceneEntities.stream().map(SceneEntity::getFileId).collect(Collectors.toSet()));
        return fileEntities.stream().map((m) -> {
            return FileVO.makeBy(m);
        }).collect(Collectors.toList());
    }

    @Override
    public List<String> getFilterParentIds() {
        QueryWrapper<SceneEntity> query = new QueryWrapper<>();
        query.lambda().eq(SceneEntity::getParentId, "0").in(SceneEntity::getCode, AlarmTreeFilterEnum.codeList());
        return baseMapper.selectObjs(query);
    }

    private FileService fileService;

    @Autowired
    public void setFileService(FileService fileService) {
        this.fileService = fileService;
    }
}
